package com.econ.powercloud.ems.service.common;

import com.econ.powercloud.ems.common.enums.TimingControlStatusEnum;
import com.econ.powercloud.ems.dal.entity.EmsTimingControl;
import com.econ.powercloud.ems.job.SchedulingRunnable;
import com.econ.powercloud.ems.service.dao.EmsTimingControlDAO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * @Author : zjf
 * @Description : Cron定时任务注册类
 * @Date : 2023/8/10 10:56
 * @Modified by : zjf
 */
@Component
@Slf4j
public class CronTaskCommonService implements SchedulingConfigurer {

    public CronTaskCommonService() {
        //线程池任务调度器
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(2);
        scheduler.setThreadNamePrefix("TaskScheduler-");
        // 保证能立刻丢弃运行中的任务
        scheduler.setRemoveOnCancelPolicy(true);
        scheduler.initialize();
        taskScheduler = scheduler;
    }

    /**
     * 自己维护一个map用于保存当前系统的定时任务，方便后续动态增删定时任务
     * key:定时任务配置表中主键id，value：ScheduledFuture对象
     */
    private static final Map<Integer, ScheduledFuture<?>> FUTURE_MAP = new ConcurrentHashMap<>();

    /**
     * 方便后期获取 future
     */
    private TaskScheduler taskScheduler;

    @Autowired
    private EmsTimingControlDAO emsTimingControlDAO;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(taskScheduler);
    }

    /**
     * 取消定时任务
     *
     * @param schedulePid 定时任务配置表中主键id
     */
    public Boolean cancel(Integer schedulePid) {
        boolean result = true;
        if (!FUTURE_MAP.containsKey(schedulePid)) {
            return result;
        }
        ScheduledFuture<?> future = FUTURE_MAP.get(schedulePid);

        if (future != null) {
            result = future.cancel(true);
        }
        log.info(schedulePid + "对应的定时任务已取消！");
        return result;
    }

    /**
     * 重启定时任务
     *
     * @param schedulePid 定时控制配置表中主键id
     */
    public Boolean addTask(Integer schedulePid) {
        EmsTimingControl emsTimingControl = emsTimingControlDAO.getById(schedulePid);
        if (emsTimingControl==null){
            return false;
        }
        String cronExpression = emsTimingControl.getCron();
        String beanName = emsTimingControl.getBeanName();
        String methodName = emsTimingControl.getMethodName();
        if (StringUtils.isEmpty(cronExpression)||StringUtils.isEmpty(beanName)||StringUtils.isEmpty(methodName)){
            return false;
        }
        //先取消定时任务
        this.cancel(schedulePid);
        Runnable task = new SchedulingRunnable(beanName, methodName, schedulePid);
        ScheduledFuture<?> schedule = taskScheduler.schedule(task, scheduledConfig -> new CronTrigger(cronExpression).nextExecutionTime(scheduledConfig));
        FUTURE_MAP.put(schedulePid, schedule);
        return true;
    }

    /**
     * 初始化已经生效的定时，放入map
     * @return
     */
    @PostConstruct
    public void init() {
        List<EmsTimingControl> list = emsTimingControlDAO.list();
        list.forEach(d->{
            if (TimingControlStatusEnum.EFFECTIVE.getCode().equals(d.getStatus())){
                //把已生效的定时放入map
                Runnable task = new SchedulingRunnable(d.getBeanName(), d.getMethodName(), d.getId());
                ScheduledFuture<?> schedule = taskScheduler.schedule(task, scheduledConfig -> new CronTrigger(d.getCron()).nextExecutionTime(scheduledConfig));
                FUTURE_MAP.put(d.getId(), schedule);
            }
        });
    }

}
